package Salesman.gui;

import Salesman.AbstractStep;
import Salesman.City;
import Salesman.Data.DataProvider;
import Salesman.TSPEngine;
import Salesman.TSPGreedy;
import Salesman.TSPInsertion;
import Salesman.TSPDynamic;
import Salesman.event.StepEvent;
import Salesman.event.StepListener;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventObject;
import javax.swing.*;
import org.jdesktop.swingx.JXTitledPanel;
import org.rlpw.swing.Filter;
import org.rlpw.swing.JOComboBox;
import org.rlpw.swing.event.ModelEvent;
import org.rlpw.swing.event.ModelListener;
import org.rlpw.swing.event.ModelReplacedEvent;

public class Simulation extends JPanel {

    private JOComboBox<City> cityComboBox;
    private JButton startButton = new JButton("Start", new Salesman.Data.Icons.ImageIcon("play.png"));
    private JButton nextButton = new JButton("Weiter", new Salesman.Data.Icons.ImageIcon("next.png"));
    private JCheckBox stepCheckBox = new JCheckBox("Einzelschritte");
    private JOComboBox<TSPEngine> algoComboBox = new AlgoComboBox();
    private SimulationMap simMap;
    private Protocol protocol = new Protocol();
    SimulationThread simThread;

    public Simulation() {

        setLayout(new BorderLayout());

        cityComboBox = new JOComboBox<City>(DataProvider.getCityData());
        cityComboBox.setFilter(new Filter<City>() {

            @Override
            public boolean isVisible(City item) {
                return item.isInSimulation();
            }
        });

        simMap = new SimulationMap();

        JToolBar bar = new JToolBar();
        bar.add(new JLabel("Verfahren: "));
        bar.add(algoComboBox);
        bar.add(new JLabel("Startpunkt: "));
        bar.add(cityComboBox);
        bar.addSeparator();
        bar.add(startButton);
        bar.add(nextButton);
        bar.add(stepCheckBox);

        cityComboBox.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                simMap.setStartCity(cityComboBox.getSelectedItem());
                simMap.paint();
            }
        });


        DataProvider.getCityData().addModelListener(new ModelListener<City>() {

            @Override
            public void itemAdded(ModelEvent<City> e) {
            }

            @Override
            public void itemRemoved(ModelEvent<City> e) {
            }

            @Override
            public void itemChanged(ModelEvent<City> e) {
                simMap.getModel().clear();
                simMap.paint();
            }

            @Override
            public void itemReplaced(ModelReplacedEvent<City> e) {
            }

            @Override
            public void allItemsRemoved(EventObject e) {
            }
        });


        nextButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (simThread != null) {
                    simThread.nextStep();
                }
            }
        });

        startButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {

                if (simThread != null) {
                    simThread.interrupt();
                }

                simThread = new SimulationThread(algoComboBox.getSelectedItem());
                simThread.start();
                checkEnabled();
            }
        });


        stepCheckBox.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                checkEnabled();
            }
        });

        JSplitPane pane1 = new JSplitPane();

        pane1.setDividerSize(6);
        pane1.setOneTouchExpandable(true);

        pane1.setDividerLocation(700);
        pane1.setOrientation(JSplitPane.HORIZONTAL_SPLIT);

        pane1.setLeftComponent(simMap);
        pane1.setRightComponent(new JXTitledPanel("Ablauf Protokoll", protocol));



        add("North", bar);
        add("Center", pane1);


        checkEnabled();
    }

    private void checkEnabled() {
        nextButton.setEnabled(stepCheckBox.isSelected() && (simThread != null && simThread.isAlive()));

    }

    class SimulationThread extends Thread {

        private boolean nextStep = false;
        private TSPEngine engine;

        public SimulationThread(TSPEngine engine) {
            this.engine = engine;
            protocol.clear();

        }

        public void nextStep() {

            if (isAlive()) {
                nextStep = true;
            }
            checkEnabled();
        }

        @Override
        public void run() {
            try {
                simMap.getModel().clear();
                protocol.clear();

                engine.start(cityComboBox.getSelectedItem());
                boolean run = true;
                while (run) {
                    if (!stepCheckBox.isSelected() || nextStep) {
                        run = engine.next();
                        nextStep = false;
                    }
                    if (stepCheckBox.isSelected()) {
                        Thread.sleep(100);
                    }
                }


            } catch (Exception ex) {
                ex.printStackTrace();
            }

            engine.start(cityComboBox.getSelectedItem());
            checkEnabled();
        }
    }

    class Protocol extends JScrollPane {

        private JTextArea protocolArea = new JTextArea();

        public Protocol() {

            protocolArea.setEditable(false);
            protocolArea.setBackground(UIManager.getColor("Panel.background"));
            setViewportView(protocolArea);
            protocolArea.setAutoscrolls(true);
        }

        public void clear() {
            protocolArea.setText("");
        }

        public void nextStep(AbstractStep step) {
            protocolArea.append(step.toString() + "\n");
            protocolArea.setCaretPosition(protocolArea.getText().length());
        }
    }

    class AlgoComboBox extends JOComboBox<TSPEngine> {

        public AlgoComboBox() {

            //register(new TSPBound());
            register(new TSPGreedy());
            register(new TSPDynamic());
            register(new TSPInsertion());
        }

        public void register(TSPEngine engine) {

            getDataModel().add(engine);
            engine.addStepListener(new StepListener() {

                @Override
                public void nextStep(StepEvent e) {
                    simMap.setModel(e.getStep().getCities());
                    protocol.nextStep(e.getStep());
                }
            });

        }
    }
}
